#pragma once

#include "jsruntimecore.h"

typedef unsigned char jsbool;
typedef unsigned int JSUint32;

#ifdef WIN32

#define nil (void *) 0
#define true 1
#define false 0

#endif

#ifdef MACOSX
id idFromObject(jscontext *cx, jsvalue obj);
#endif

struct jsstring
{
	int flags;
	unsigned short *chars;
	int length;
#ifdef MACOSX
	NSString *nsstr;
#endif
	struct jsstring *prevString, *nextString;
};

struct jsfield {
	struct jsfield *next;
	int hash;
	jsvalue field;
	jsvalue val, getter, setter;
};

struct jsobject {
	int flags;
	struct jsfunction *cls;
	struct jsobject *prevObject, *nextObject;
	struct jsfield *fields;
	const char *origin;
};

struct jsfunction
{
	struct jsobject base;
	jsvalue (*proc)(jscontext *, jsframe *, jsvalue, jsvalue *, int);
	void (*destructor)(jscontext *, jsvalue obj);
	jsvalue (*customGet)(jscontext *, jsvalue obj, int h, jsvalue key);
	void (*customSet)(jscontext *, jsvalue obj, int h, jsvalue key, jsvalue v);
	jsvalue (*customInvoke)(jscontext *, jsvalue func, jsvalue thisArg, jsvalue *params, int paramCount);
	jsvalue (*customGetAttr)(jscontext *, jsvalue obj, int h, jsvalue key);
	jsframe *outer;
	struct jsobject *prototype;
};

#define FLAG_MARK 1
#define FLAG_DELETED 2
#define FLAG_CONSTANT 8
#define FLAG_FUNCTION 256
#define FLAG_ARRAY 512

/*
 * Type tags stored in the low bits of a jsval.
 */
#define JSVAL_OBJECT            0x0     /* untagged reference to object */
#define JSVAL_INT               0x1     /* tagged 31-bit integer value */
#define JSVAL_DOUBLE            0x2     /* tagged reference to double */
#define JSVAL_STRING            0x4     /* tagged reference to string */
#define JSVAL_BOOLEAN           0x6     /* tagged boolean value */

/* Type tag bitfield length and derived macros. */
#define JS_BIT(n)       ((JSUint32)1 << (n))
#define JS_BITMASK(n)   (JS_BIT(n) - 1)
#define JSVAL_TAGBITS           3
#define JSVAL_TAGMASK           JS_BITMASK(JSVAL_TAGBITS)
#define JSVAL_TAG(v)            ((v) & JSVAL_TAGMASK)
#define JSVAL_SETTAG(v,t)       ((v) | (t))
#define JSVAL_CLRTAG(v)         ((v) & ~(jsvalue)JSVAL_TAGMASK)
#define JSVAL_ALIGN             JS_BIT(JSVAL_TAGBITS)

/* Predicates for type testing. */
#define JSVAL_IS_OBJECT(v)      (JSVAL_TAG(v) == JSVAL_OBJECT && validateObject(v))
#define JSVAL_IS_NUMBER(v)      (JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v))
#define JSVAL_IS_INT(v)         (((v) & JSVAL_INT) && (v) != JSVAL_NULL)
#define JSVAL_IS_DOUBLE(v)      (JSVAL_TAG(v) == JSVAL_DOUBLE)
#define JSVAL_IS_STRING(v)      (JSVAL_TAG(v) == JSVAL_STRING)
#define JSVAL_IS_BOOLEAN(v)     (JSVAL_TAG(v) == JSVAL_BOOLEAN)
#define JSVAL_IS_NULL(v)        ((v) == JSVAL_NULL)
#define JSVAL_IS_VOID(v)        ((v) == JSVAL_VOID)
#define JSVAL_IS_PRIMITIVE(v)   (!JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v))

#define OBJECT_TO_JSVAL(obj)    ((jsvalue)(obj))
#define DOUBLE_TO_JSVAL(dp)     JSVAL_SETTAG((jsvalue)(dp), JSVAL_DOUBLE)
#define STRING_TO_JSVAL(str)    JSVAL_SETTAG((jsvalue)(str), JSVAL_STRING)

#define JSVAL_TO_BOOLEAN(v)     ((jsbool)((v) >> JSVAL_TAGBITS))
#define BOOLEAN_TO_JSVAL(b)     JSVAL_SETTAG((jsvalue)(b) << JSVAL_TAGBITS, JSVAL_BOOLEAN)

#define JSVAL_IS_GCTHING(v)     (!((v) & JSVAL_INT) && !JSVAL_IS_BOOLEAN(v))
#define JSVAL_TO_GCTHING(v)     ((void *)JSVAL_CLRTAG(v))
#define JSVAL_TO_OBJECT(v)      ((jsobject *)JSVAL_TO_GCTHING(v))
#define JSVAL_TO_DOUBLE(v)      ((jsdouble *)JSVAL_TO_GCTHING(v))
#define JSVAL_TO_STRING(v)      ((jsstring *)JSVAL_TO_GCTHING(v))

#define JSVAL_TO_INT(v)         ((int)(v) >> 1)

void jsaddroot(jscontext *cx, jsvalue *ref);
void jsaddrootmulti(jscontext *cx, jsvalue *ref, int count);
void jsremoveroot(jscontext *cx, jsvalue *ref);

void jsaddstackroot(jscontext *cx, jsvalue ref);

void debug(const char *name, jsvalue v);

int validateObject(jsvalue v);

jsvalue bindFunctionHint(jscontext *cx, jsvalue (*proc)(jscontext *, jsframe *, jsvalue, jsvalue *, int), const char *hint);
jsvalue jsclass(jscontext *cx, jsvalue (*constructor)(jscontext *, jsframe *, jsvalue, jsvalue *, int), jsvalue superClass);
void jsadddynamicfield(jscontext *cx, jsvalue cls, int h, const char *fieldName, jsvalue (*getter)(jscontext *, jsframe *, jsvalue, jsvalue *, int), jsvalue (*setter)(jscontext *, jsframe *, jsvalue, jsvalue *, int));
void jsaddfield(jscontext *cx, jsvalue cls, int h, const char *fieldName, jsvalue val);

jsvalue jsinvokename(jscontext *cx, jsvalue thisArg, const char *name, jsvalue *params, int paramCount);
void jssetdynamic(jscontext *cx, jsvalue v, int h, const char *fieldName, jsvalue (*getter)(jscontext *, jsframe *, jsvalue, jsvalue *, int), jsvalue (*setter)(jscontext *, jsframe *, jsvalue, jsvalue *, int));

void jsdelete(jscontext *cx, jsvalue v, int h, jsvalue elem);
int jsgetint(jscontext *cx, jsvalue v, int h,  const char *elem, int defaultValue);
double jsgetdouble(jscontext *cx, jsvalue v, int h,  const char *elem, double defaultValue);
jsbool jsgetbool(jscontext *cx, jsvalue obj, int h,  const char *elem, jsbool defaultValue);
void jscopy(jscontext *cx, jsvalue destObj, jsvalue srcObj, int h, const char *fieldName);

jsvalue jsgetslot(jscontext *cx, jsvalue v, int h,  const char *elem);
void jssetslot(jscontext *cx, jsvalue v, int h, const char *elem, jsvalue v2);
void copyParams(jsframe *f, int paramCount, jsvalue *argv, int argc);
void trace(jscontext *cx);

#ifdef MACOSX
NSString *jsasstrRetain(jscontext *cx, jsvalue v);
const char *keyFromString(NSString *str);
NSString *jsgetstring(jscontext *cx, jsvalue v, int h,  const char *elem, NSString *defaultValue);
jsvalue jsstrFromNSString(jscontext *cx, NSString *strval);
#endif
jsvalue jsstrnconst(jscontext *cx, unsigned short *constStr, int len);

jsvalue jsstrFromChars(jscontext *cx, unsigned short *chars, int length);
jsvalue jsstrFromConst(jscontext *cx, void *chars);
jsvalue jsstrFromCstring(jscontext *cx, const char *cstr);

void jsmark(jscontext *cx, jsvalue v);
void jsgc(jscontext *cx);
double jsasnum(jscontext *cx, jsvalue v);
int jsasint(jscontext *cx, jsvalue v);

jsbool isArray(jsvalue v);
int jsarraylength(jsvalue v);
jsvalue jsarrayelem(jsvalue v, int idx);

jsbool jsasbool(jscontext *cx, jsvalue v);
void markGlobals();

#ifdef MACOSX
void idToObject(jscontext *cx, jsvalue obj, id val);
#endif

int equalStringsJC(jsstring *a, const char *b);
